package com.dnake.fragment;

import android.app.DialogFragment;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.alibaba.fastjson.JSON;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.arcsoft.facetracking.AFT_FSDKEngine;
import com.arcsoft.facetracking.AFT_FSDKError;
import com.arcsoft.facetracking.AFT_FSDKFace;
import com.arcsoft.facetracking.AFT_FSDKVersion;
import com.dnake.Constant;
import com.dnake.WeKerApplication;
import com.dnake.db.UserBeanDao;
import com.dnake.entity.BaseHttpBean;
import com.dnake.entity.EventBusBean;
import com.dnake.entity.FaceResultBean;
import com.dnake.entity.OpenHistoryBean;
import com.dnake.entity.UserBean;
import com.dnake.misc.SysAccess;
import com.dnake.misc.SysTalk;
import com.dnake.panel.MainActivity;
import com.dnake.panel.R;
import com.dnake.utils.DateUtil;
import com.dnake.utils.FaceDB;
import com.dnake.utils.FileUtils;
import com.dnake.utils.ImageUtil;
import com.dnake.utils.SpeechUtils;
import com.dnake.utils.ToastUtil;
import com.guo.android_extend.java.AbsLoop;
import com.guo.android_extend.java.ExtByteArrayOutputStream;
import com.guo.android_extend.tools.CameraHelper;
import com.guo.android_extend.widget.CameraFrameData;
import com.guo.android_extend.widget.CameraGLSurfaceView;
import com.guo.android_extend.widget.CameraSurfaceView;
import com.orhanobut.logger.Logger;
import com.wx.android.common.util.SharedPreferencesUtils;

import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
import org.xutils.common.Callback;
import org.xutils.http.RequestParams;
import org.xutils.x;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import butterknife.Bind;
import butterknife.ButterKnife;

/**
 * @author haoyuliu
 * @since 2018/3/10
 */
public class FaceDetecterFragment extends DialogFragment implements CameraSurfaceView.OnCameraListener, View.OnTouchListener, Camera.AutoFocusCallback {

    private final String TAG = this.getClass().getSimpleName();

    @Bind(R.id.surfaceView)
    CameraSurfaceView surfaceView;
    @Bind(R.id.glsurfaceView)
    CameraGLSurfaceView glsurfaceView;
    private int mWidth, mHeight, mFormat;
    private Camera mCamera;
    AFT_FSDKVersion version = new AFT_FSDKVersion();
    AFT_FSDKEngine engine = new AFT_FSDKEngine();
    List<AFT_FSDKFace> result = new ArrayList<>();

    int mCameraID;
    int mCameraRotate;
    boolean mCameraMirror;
    byte[] mImageNV21 = null;
    FRAbsLoop mFRAbsLoop = null;
    AFT_FSDKFace mAFT_FSDKFace = null;
    private boolean isFaceRecognition = false;

    private View mView;
    private ScanFaceTimer mScanFaceTimer;
    private int faceNum = 0;//人脸识别失败次数
    private int frameCount = 0;//摄像头帧数
    private boolean isLight = false;//补光灯
    private boolean isOpenDoor = false;//是否开门成功
    private String userId;//用户id
    private int offset = 40;
    private boolean isFaceFar = false;
    private UserBeanDao mUserBeanDao;
    public static FaceDetecterFragment newInstance() {
        return new FaceDetecterFragment();
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE| DialogFragment.STYLE_NO_FRAME,0);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_face_tracker, container, false);
        ButterKnife.bind(this, mView);
        initData();
        mUserBeanDao = WeKerApplication.getContext().getDaoSession().getUserBeanDao();

        return mView;
    }

    private void initData() {
        mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;
        mCameraRotate = 0;
        mCameraMirror = false;
        mWidth = 320;
        mHeight = 240;
//        mWidth = 640;
//        mHeight = 480;
        mFormat = ImageFormat.NV21;
        glsurfaceView.setOnTouchListener(this);
        surfaceView.setOnCameraListener(this);
        surfaceView.setupGLSurafceView(glsurfaceView, true, mCameraMirror, mCameraRotate);
        surfaceView.debug_print_fps(true, false);
        mScanFaceTimer = new ScanFaceTimer(20000, 1000);
        mScanFaceTimer.start();

        AFT_FSDKError err = engine.AFT_FSDK_InitialFaceEngine(FaceDB.appid, FaceDB.ft_key, AFT_FSDKEngine.AFT_OPF_0_HIGHER_EXT, 16, 5);
        Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode());
        err = engine.AFT_FSDK_GetVersion(version);
        Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode());
        mFRAbsLoop = new FRAbsLoop();
        mFRAbsLoop.start();

    }


    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 101:
                    SysAccess.unlock(0);
                    ToastUtil.showSuccessView("开门成功");
                    if (isAdded())
                        SpeechUtils.getInstance(getActivity()).speakText(getString(R.string.speed_open_success));
                    break;
                case 102:
                    //刷脸超过五次
                    if (faceNum>=5){
                        faceNum=0;
                        if (isAdded())
                            SpeechUtils.getInstance(getActivity()).speakText(getString(R.string.speed_verification_failed));
                        dismiss();
                    }else {
                        SysTalk.touch("X");
                        isFaceRecognition = false;
                    }
                    break ;
                    case 103:

                        if (isAdded()&&!isFaceFar){
                            isFaceFar = true;
                            SpeechUtils.getInstance(getActivity()).speakText(getResources().getString(R.string.speed_two));
                        }
                        ToastUtil.show("请靠近镜头！");
                        break;
                case 104:
                    ToastUtil.showLong("网络中断！");
                    SysAccess.unlock(0);
                    if (isAdded())
                        SpeechUtils.getInstance(getActivity()).speakText(getString(R.string.speed_open_success));
                    dismiss();
//                        SpeechUtils.getInstance(getActivity()).speakText(getString(R.string.speed_network_interruption));
                    break;
            }
            return false;
        }
    });
    /**
     * 超时关闭
     */
    class ScanFaceTimer extends CountDownTimer {
        public ScanFaceTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
        }

        @Override
        public void onFinish() {
            dismiss();
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.unbind(this);
        if (isLight){
            SysAccess.setLight(0);
        }
        mFRAbsLoop.shutdown();
        AFT_FSDKError err = engine.AFT_FSDK_UninitialFaceEngine();
        EventBus.getDefault().post(new EventBusBean(Constant.EVENTBUS_FACE_DETECT_CLOSE,""));
    }

    class FRAbsLoop extends AbsLoop {

        @Override
        public void setup() {
        }

        @Override
        public void loop() {
            if (mImageNV21 != null) {
                if (isFaceRecognition) {
                    mImageNV21 = null;
                    return;
                }
                Log.e(TAG, "loop: 面积"+ mAFT_FSDKFace.getRect().width()*mAFT_FSDKFace.getRect().height());
                if (mAFT_FSDKFace.getRect().width()*mAFT_FSDKFace.getRect().height()<3000){
                    mHandler.sendEmptyMessage(103);
                    mImageNV21 = null;
                    return;
                }


                isFaceRecognition = true;
                byte[] data = mImageNV21;
                YuvImage yuv = new YuvImage(data, ImageFormat.NV21, mWidth, mHeight, null);
                ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
                Rect rect = new Rect(0, 0, 0, 0);
                rect.left = mAFT_FSDKFace.getRect().left - offset < 0 ? 0 : mAFT_FSDKFace.getRect().left - offset;
                rect.top = mAFT_FSDKFace.getRect().top - offset < 0 ? 0 : mAFT_FSDKFace.getRect().top - offset;
                rect.right = Math.abs(mAFT_FSDKFace.getRect().right + offset) > mWidth ? mWidth : mAFT_FSDKFace.getRect().right + offset;
                rect.bottom = mAFT_FSDKFace.getRect().bottom + offset > mHeight ? mHeight : mAFT_FSDKFace.getRect().bottom + offset;
                yuv.compressToJpeg(rect, 80, ops);
                final Bitmap bmp = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
                try {
                    ops.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                final String fileName = System.currentTimeMillis()+""+UUID.randomUUID()+ ".jpg";
                String faceFilePath = Constant.FILE_DIR + fileName;
                FileUtils.saveSnapshotBitmap(bmp, fileName,80);
                if (bmp!=null){
                    bmp.recycle();
                }
                String objectKey = "face/"+DateUtil.currentDate()+"/"+fileName;
                uploadFile(WeKerApplication.getContext().getOss(),faceFilePath,objectKey,1);
//                uploadPhoto(new File(faceFilePath),1);
                identifyUser(faceFilePath);
                mImageNV21 = null;
            }

        }

        @Override
        public void over() {
        }
    }


    @Override
    public void onAutoFocus(boolean success, Camera camera) {

    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        CameraHelper.touchFocus(mCamera, event, v, this);
        return false;
    }

    @Override
    public Camera setupCamera() {
        if (mCamera!=null){
            mCamera.release();
            mCamera = null;
        }
        if (!MainActivity.mOK){
            dismiss();
        }
        mCamera = Camera.open(mCameraID);
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mWidth, mHeight);
            parameters.setPreviewFormat(mFormat);

            for( Camera.Size size : parameters.getSupportedPreviewSizes()) {
                Log.d(TAG, "SIZE:" + size.width + "x" + size.height);
            }
            for( Integer format : parameters.getSupportedPreviewFormats()) {
                Log.d(TAG, "FORMAT:" + format);
            }

            List<int[]> fps = parameters.getSupportedPreviewFpsRange();
            for(int[] count : fps) {
                Log.d(TAG, "T:");
                for (int data : count) {
                    Log.d(TAG, "V=" + data);
                }
            }
            //parameters.setPreviewFpsRange(15000, 30000);
            //parameters.setExposureCompensation(parameters.getMaxExposureCompensation());
            //parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
            //parameters.setAntibanding(Camera.Parameters.ANTIBANDING_AUTO);
//            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            //parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
            //parameters.setColorEffect(Camera.Parameters.EFFECT_NONE);
            mCamera.setParameters(parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (mCamera != null) {
            mWidth = mCamera.getParameters().getPreviewSize().width;
            mHeight = mCamera.getParameters().getPreviewSize().height;
        }
        return mCamera;
    }

    @Override
    public void setupChanged(int format, int width, int height) {

    }

    @Override
    public boolean startPreviewLater() {
        return false;
    }

    @Override
    public Object onPreview(byte[] data, int width, int height, int format, long timestamp) {
        AFT_FSDKError err = engine.AFT_FSDK_FaceFeatureDetect(data, width, height, AFT_FSDKEngine.CP_PAF_NV21, result);

        frameCount++;
        if (frameCount==1||isOpenDoor){
            YuvImage yuv = new YuvImage(data, ImageFormat.NV21, width, height, null);
            ExtByteArrayOutputStream ops = new ExtByteArrayOutputStream();
            yuv.compressToJpeg( new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), 100, ops);
            Bitmap bmp = BitmapFactory.decodeByteArray(ops.getByteArray(), 0, ops.getByteArray().length);
            try {
                ops.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (isOpenDoor){
                isOpenDoor = false;
                //取一帧图片上传开门记录
                final String fileName = System.currentTimeMillis()+""+UUID.randomUUID()+ ".jpg";
                String faceFilePath = Constant.FILE_DIR + fileName;
                FileUtils.saveSnapshotBitmap(bmp, fileName,80);
                if (bmp!=null){
                    bmp.recycle();
                }
                Log.e(TAG, "loop: "+System.currentTimeMillis());
                String objectKey = "face/"+DateUtil.currentDate()+"/"+fileName;
                uploadFile(WeKerApplication.getContext().getOss(),faceFilePath,objectKey,2);
            }
            else{
                if (ImageUtil.getBright(bmp)<120){
                    isLight = true;
                    SysAccess.setLight(1);
                }
            }

        }
        if (mImageNV21 == null) {
            //取人脸框面积的最大值取帧
            if (!result.isEmpty()) {
                int max=0;
                int i = 0;
                for (int j = 0; j < result.size(); j++) {
                    int area = result.get(j).getRect().width()*result.get(j).getRect().height();
                    if (area>max){
                        max = area;
                        i=j;
                    }
                }
                mAFT_FSDKFace = result.get(i).clone();
                mImageNV21 = data.clone();
            }
        }
        Rect[] rects = new Rect[result.size()];
        for (int i = 0; i < result.size(); i++) {
            rects[i] = new Rect(result.get(i).getRect());
        }
        result.clear();
        return rects;
    }

    @Override
    public void onBeforeRender(CameraFrameData data) {

    }

    @Override
    public void onAfterRender(CameraFrameData data) {
        glsurfaceView.getGLES2Render().draw_rect((Rect[])data.getParams(), Color.GREEN, 2);

    }


    /**
     * 人脸识别
     *
     * @param filePath
     */
    private void identifyUser(String filePath) {
        long older = System.currentTimeMillis();
        Log.e(TAG, "loop: "+ older);

        if (!SysTalk.netState){
            return;
        }
        HashMap<String, Object> options = new HashMap<>();
        options.put("user_top_num", 1);
        options.put("ext_fields", "faceliveness");
        JSONObject res = null;
        try {
            //人脸识别
            res = WeKerApplication.client.identifyUser(Arrays.asList("xq"+SharedPreferencesUtils.getString(Constant.SP_COMMUNITY_ID)), filePath, options);

        }catch (Throwable e){
            Logger.e(e.getMessage());
            dismiss();
        }
        final FaceResultBean face = JSON.parseObject(res.toString(), FaceResultBean.class);
        long newer = System.currentTimeMillis();
        Logger.e(  "loop: "+ newer);

        // TODO: 2019/9/9 检查晚上开不了门的问题
        if (newer-older>=4000){
            ToastUtil.showFailView("比对超时");
        }

        if (face.getResult_num() > 0) {
            if (face.getResult().get(0).getScores().get(0) > SharedPreferencesUtils.getInt(Constant.SP_FACE_SCORE)) {
                //判断活体检测
                if (SharedPreferencesUtils.getBoolean(Constant.SP_FACE_LIVE)){
                    Log.e(TAG, "identifyUser: "+face.getExt_info().getFaceliveness());
                    if (Float.parseFloat(face.getExt_info().getFaceliveness())*100>SharedPreferencesUtils.getInt(Constant.SP_FACE_SCORE)){
                        userId = face.getResult().get(0).getUid();
                        // TODO: 2019/11/7 跳过人脸权限
//                        isOpenDoor = true;
//                        mHandler.sendEmptyMessage(101);
                        getUserEffective(userId);
                    }else {
                        faceNum++;
                        mHandler.sendEmptyMessageDelayed(102, 1000);
                    }
                }else {
                    userId = face.getResult().get(0).getUid();
                    // TODO: 2019/11/7  跳过人脸权限
//                    isOpenDoor = true;
//                    mHandler.sendEmptyMessage(101);
                    getUserEffective(userId);
                }
            }else {
                faceNum++;
                mHandler.sendEmptyMessageDelayed(102, 1000);
            }
        }else {
            faceNum++;
            mHandler.sendEmptyMessageDelayed(102, 1000);
        }
    }


    /**
     * 上传图片到阿里云
     * @param oss
     * @param path
     * @param objectKey
     * type  1检测到人脸上传   2  开门成功后上传
     */
    private void uploadFile(OSS oss, String path, final String objectKey, final int type){
        if (!SysTalk.netState){
            mHandler.sendEmptyMessage(104);
            return;
        }
        // 构造上传请求
        PutObjectRequest put = new PutObjectRequest(Constant.BUCKET_NAME, objectKey, path);

        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
            }
        });

        OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
            @Override
            public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                String imgUrl = "https://" + Constant.BUCKET_NAME+"."+Constant.OSS_ENDPOINT+ File.separator+objectKey;
                if (type==2){
                        OpenHistoryBean openHistoryBean = new OpenHistoryBean();
                        openHistoryBean.setSnapshot(imgUrl);
                        openHistoryBean.setType("3");
                        openHistoryBean.setUserId(userId);
                        EventBus.getDefault().post(openHistoryBean);
                        dismiss();
                    }else {
                        uploadFace(imgUrl);
                    }
                Logger.e(imgUrl);
            }

            @Override
            public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                // 请求异常
                if (clientExcepion != null) {
                    // 本地异常如网络异常等
                    clientExcepion.printStackTrace();
                }
                if (serviceException != null) {
                    // 服务
                    Log.e("ErrorCode", serviceException.getErrorCode());
                    Log.e("RequestId", serviceException.getRequestId());
                    Log.e("HostId", serviceException.getHostId());
                    Log.e("RawMessage", serviceException.getRawMessage());
                }
            }
        });
    }

    /**
     * 上传人脸抓拍
     * @param faceUrl
     */
    private void uploadFace(String faceUrl) {
        if (!SysTalk.netState){
            return;
        }
        RequestParams params = new RequestParams(Constant.HTTP_URL + "/device/snapshot/saveOrUpdate");
        params.setAsJsonContent(true);
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("deviceId", SharedPreferencesUtils.getString(Constant.SP_DEVICEID));
            jsonObject.put("faceimage", faceUrl);

            params.setBodyContent(jsonObject.toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        x.http().post(params, new Callback.CommonCallback<String>() {
            @Override
            public void onCancelled(CancelledException arg0) {

            }

            @Override
            public void onError(Throwable throwable, boolean arg1) {
                throwable.printStackTrace();
            }

            @Override
            public void onFinished() {
            }

            @Override
            public void onSuccess(String response) {
                Logger.json(response);
            }
        });
    }

    /**
     * 检验用户开门权限
     */
    private void getUserEffective(String userId) {
        if(SharedPreferencesUtils.getInt(Constant.SP_PERMISSION)==1){
//            离线检测人脸权限
            long startTime = System.currentTimeMillis();
            UserBean userBean = mUserBeanDao.load(Long.parseLong(userId));
            Log.d("ww","userBean= "+userBean);
            Logger.t("ww").d("userBean= "+userBean);
            if (userBean!=null){
                if (Integer.parseInt(userBean.getEffectiveType())==1&&Long.parseLong(userBean.getEffectiveEndTime())< DateUtil.millis()){
                    faceNum++;
                    mHandler.sendEmptyMessageDelayed(102, 1000);

                    return;
                }else {
                    isOpenDoor = true;
                    mHandler.sendEmptyMessage(101);
                }
            }else {
                faceNum++;
                mHandler.sendEmptyMessageDelayed(102, 1000);
            }

            long endTime = System.currentTimeMillis();
            String currentTime = DateUtil.getMillon(startTime);
            if (endTime-startTime>500) {
                String timeTest = SharedPreferencesUtils.getString("timeTest");
                SharedPreferencesUtils.put("timeTest", timeTest + "|" + currentTime + "_" + ((endTime - startTime) / 1000));
            }

        }else {
//            final long olderTime = System.currentTimeMillis();
            //在线验证
            RequestParams params = new RequestParams(Constant.HTTP_URL + "/community/device/" + SharedPreferencesUtils.getString(Constant.SP_DEVICEID)+"/"+userId+"/3/effective");
            x.http().get(params, new Callback.CommonCallback<String>() {
                @Override
                public void onCancelled(CancelledException arg0) { }

                @Override
                public void onError(Throwable throwable, boolean arg1) {
                    throwable.printStackTrace();
                    ToastUtil.showFailView("云警在线用户权限判断错误");
                    Logger.e("云警在线用户权限判断错误");
                }

                @Override
                public void onFinished() {

                }

                @Override
                public void onSuccess(String response) {
//                    long newerTime = System.currentTimeMillis();
//                    if (newerTime-olderTime>4000){
//                        Logger.e("云警在线用户权限判断  超时");
//                        ToastUtil.showFailView("云警在线用户权限判断  超时");
//                    }
                    Logger.json(response);

                    if (!TextUtils.isEmpty(response)) {
                        BaseHttpBean baseHttpBean = JSON.parseObject(response,BaseHttpBean.class);
                        if (baseHttpBean.getData().equals("1")){
                            isOpenDoor = true;
                            mHandler.sendEmptyMessage(101);
                        }else {
                            faceNum++;
                            mHandler.sendEmptyMessageDelayed(102, 1000);
                        }
                    }
                }
            });
        }

    }
}
